using System;
using System.Xml;
using System.Xml.XPath;
using System.Globalization;

namespace Dida
{
	public class XmlPositionMarker
	{
		int line = -1;
		int pos = -1;

		public XmlPositionMarker(XmlReader reader)
		{
			if (reader is IXmlLineInfo)
			{
				IXmlLineInfo lineInfo = (IXmlLineInfo)reader;
				line = lineInfo.LineNumber;
				pos = lineInfo.LinePosition;
			}
		}

		public XmlPositionMarker(XPathNavigator navigator)
		{
			if (navigator is IXmlLineInfo)
			{
				IXmlLineInfo lineInfo = (IXmlLineInfo)navigator;
				line = lineInfo.LineNumber;
				pos = lineInfo.LinePosition;
			}
		}

		public static XmlPositionMarker GetAttrPosition(XmlReader reader, string attrName)
		{
			reader.MoveToAttribute(attrName);
			XmlPositionMarker ret = new XmlPositionMarker(reader);
			reader.MoveToElement();
			return ret;
		}

		public static XmlPositionMarker GetAttrPosition(XPathNavigator navigator, string attrName)
		{
			navigator.MoveToAttribute(attrName,"");
			XmlPositionMarker ret = new XmlPositionMarker(navigator);
			navigator.MoveToParent();
			 return ret;
		}

		public int LineNumber
		{
			get { return line; }
		}

		public int LinePosition
		{
			get { return pos; }
		}
	}

	public class XmlReaderException : Exception
	{
		string msg;
		XmlPositionMarker marker;

		public XmlReaderException(string msg, XmlReader reader)
		{
			this.msg = msg;
			this.marker = new XmlPositionMarker(reader);
		}

		public XmlReaderException(string msg,XmlPositionMarker marker)
		{
			this.msg = msg;
			this.marker = marker;
		}

		public XmlReaderException(string msg, XPathNavigator navigator)
		{
			this.msg = msg;
			this.marker = new XmlPositionMarker(navigator);
		}

		public XmlReaderException(Exception original, XmlReader reader)
		{
			this.msg = original.Message;
			this.marker = new XmlPositionMarker(reader);
		}

		public XmlReaderException(Exception original, XmlPositionMarker marker)
		{
			this.msg = original.Message;
			this.marker = marker;
		}

		public XmlReaderException(Exception original, XPathNavigator navigator)
		{
			this.msg = original.Message;
			this.marker = new XmlPositionMarker(navigator);
		}

		public override string Message
		{
			get
			{
				if (marker != null && marker.LineNumber != -1)
				{
					return msg + " Line: " + marker.LineNumber.ToString() +
						" Position: " + marker.LinePosition.ToString();
				}
				else
					return msg;
			}
		}
	}

	public class XmlAttributeException : XmlReaderException
	{
		public XmlAttributeException(string msg, XmlReader reader,string attrName)
			: base(msg,XmlPositionMarker.GetAttrPosition(reader,attrName))
		{
		}

		public XmlAttributeException(Exception original, XmlReader reader,string attrName)
			: base(original, XmlPositionMarker.GetAttrPosition(reader, attrName))
		{
		}

		public XmlAttributeException(string msg, XPathNavigator navigator, string attrName)
			: base(msg, XmlPositionMarker.GetAttrPosition(navigator, attrName))
		{
		}

		public XmlAttributeException(Exception msg, XPathNavigator navigator, string attrName)
			: base(msg, XmlPositionMarker.GetAttrPosition(navigator, attrName))
		{
		}
	}

	public class XmlAttributeNotSpecifiedException : XmlReaderException
	{
		private static string FormatMessage(string attrName)
		{
			return "The attribute \"" + attrName + "\" is required, but was not specified";
		}

		public XmlAttributeNotSpecifiedException(XmlReader reader,string attrName)
			: base(FormatMessage(attrName),reader)
		{
		}

		public XmlAttributeNotSpecifiedException(XmlPositionMarker marker,string attrName)
			: base(FormatMessage(attrName),marker)
		{
		}

		public XmlAttributeNotSpecifiedException(XPathNavigator navigator,string attrName)
			: base(FormatMessage(attrName), navigator)
		{
		}
	}

	public static class XmlReadingTools
	{
		private static float ReadAttributeFloat(string value, string attrName, XmlPositionMarker errorPos)
		{
			if (value == null)
				throw new XmlAttributeNotSpecifiedException(errorPos, attrName);

			try
			{
				float ret = float.Parse(value, CultureInfo.InvariantCulture);
				return ret;
			}
			catch (Exception err)
			{
				throw new XmlReaderException(err, errorPos);
			}
		}

		public static float ReadAttributeFloat(XmlReader reader, string attrName)
		{
			return ReadAttributeFloat(reader.GetAttribute(attrName), 
				attrName, new XmlPositionMarker(reader));
		}

		public static float ReadAttributeFloat(XPathNavigator reader, string attrName)
		{
			return ReadAttributeFloat(reader.GetAttribute(attrName,""),
				attrName, new XmlPositionMarker(reader));
		}

		private static int ReadAttributeInt(string value, string attrName, XmlPositionMarker errorPos)
		{
			if (value == null)
				throw new XmlAttributeNotSpecifiedException(errorPos, attrName);

			try
			{
				int ret = int.Parse(value, CultureInfo.InvariantCulture);
				return ret;
			}
			catch (Exception err)
			{
				throw new XmlReaderException(err, errorPos);
			}
		}

		public static int ReadAttributeInt(XmlReader reader, string attrName)
		{
			return ReadAttributeInt(reader.GetAttribute(attrName),
				attrName, new XmlPositionMarker(reader));
		}

		public static int ReadAttributeInt(XPathNavigator reader, string attrName)
		{
			return ReadAttributeInt(reader.GetAttribute(attrName, ""),
				attrName, new XmlPositionMarker(reader));
		}
	}
}